home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / objcissu.lha / method-lookup < prev    next >
Text File  |  1993-03-01  |  31KB  |  658 lines

  1. Date: Tue, 15 Sep 92 14:04:15 PDT
  2. From: Bruce Nilo <bruce@ictv.com>
  3. To: gnu-objc@prep.ai.mit.edu
  4. Subject: Re: Protocols, Reflection...objc_msgSend()
  5.  
  6. I think that the ability to override objc_msgSend is just
  7. one aspect of the general desire to be able to specialize various runtime behaviors. CLOS provides something called the meta object
  8. kernel which enables advanced programmers to customize the
  9. the language itself to the point that the default semantics becomes
  10. something entirely different.
  11.  
  12. A prior suggestion was made that ideally the runtime should be
  13. object oriented. In my experience this is quite doable and, if done,
  14. provides a straight forward means to override a whole host of
  15. behaviors from message dispatch to instance variable lookup. Moreover
  16. this behavior would be customized on a per class basis as opposed to
  17. globally. New "root" objects could be selectively implemented each overriding default low level runtime behaviors defined in Object.
  18.  
  19. One reason this approach is often eshewed is because supporting this
  20. level of generality is perceived to impose to great a hit on runtime
  21. performance for the majority of applications.  This is a valid
  22. criticism but can be obviated with some relatively straight forward
  23. compile time optimizations. The most important of these, IMHO, would
  24. provide for static message dispatching. For  example, if an
  25. implementor knows that a message dispatch can be  resolved at a
  26. compile time they could conceivably indicate this by a  clause such
  27. as [static-self msg: ...] instead of [self msg: ...].
  28.  
  29. In any event, as far as this project is concerned, I think that an
  30. object oriented runtime is probably best left for a future release.
  31. Other interim runtime improvements which improve functionality with
  32. a minimal time hit, such as Kim's suggestion might well be worth
  33. doing. Much of this will become more easily evaluated once we can
  34. all see the runtime version which is nearing the point of public
  35. release.
  36.  
  37. Bruce D. Nilo
  38. VP Software Systems
  39. ICTV
  40.  
  41. Date: Wed, 16 Sep 92 01:33:26 -0400
  42. From: athan@object.com (Andrew C . Athan)
  43. To: Charles Lloyd <uunet!wiltel!clloyd@uunet.uu.net>
  44. Subject: Re: Protocols, Reflection...objc_msgSend()
  45. Cc: gnu-objc@prep.ai.mit.edu
  46.  
  47.  
  48. > [talk of being able to change objc_msgSend through a function pointer]
  49.  
  50. You have my vote for flexibility in the message sending mechanism.  My only concern would be efficiency.  An extra pointer dereference per method call may or may not be negligible.  One cool way to possibly avoid this problem is to have the objc_msgSend function be rld'ed (run time linked) from someplace (e.g., a section of the executable image, a file specified in the environment, whatever).  This way, you get to put in the function you want, and it is exactly as efficient as it used to be.  There are disadvantages to this method:  how do you gain access to the old objc_msgSend?
  51.  
  52. Andrew Athan
  53.  
  54. Date: Sat, 19 Sep 92 15:01:02 -0400
  55. From: athan@object.com (Andrew C . Athan)
  56. To: gnu-objc@prep.ai.mit.edu
  57. Subject: Good article on Method Dispatch technique
  58.  
  59.  
  60. Check out September's "Journal of Object Oriented Programming" (an ACM SIGS publication).  There's an article entitled " Efficient Algorithms for Method Dispatch" that presents methods possibly applicable to the GNU Obj-C runtime.
  61.  
  62. Andrew Athan
  63.  
  64. From: Steve_Naroff@NeXT.COM (Steve Naroff)
  65. Date: Mon, 21 Sep 92 15:45:48 -0700
  66. To: Geoffrey S. Knauth <gsk@marble.com>
  67. Subject: extending message lookup vs. open coding
  68. Cc: gnu-objc@prep.ai.mit.edu
  69.  
  70.  
  71. Allowing applications to conveniently override objc_msgSend() is a very useful feature that underscores the benefits of having a central dispatcher. I don't like the idea of "open coding" calls to objc_msgSend(), it is not clear that this will result in any noticeable performance improvement (it could result in significant code expansion and hurt performance, who knows).
  72.  
  73. The pre/post operation example that Kim Christensen outlined sounds good, however it only works for methods that return "id's". objc_msgSend reuses the current stack frame, rather than create its own, this complicates implementing the post operation. All of these are implementation details that can be nailed down...
  74.  
  75. If the goal is making the message dispatcher easier to customize, writing the assembler magic to reuse the stack frame (and not disrupt any registers used by the compiler) might be intimidating to many application writers. Separating "lookup" from "dispatch" removes the assembler hacking (however makes the post op functionality hard).
  76.  
  77.     extern id (*objc_msgLookup(id, SEL))(id, SEL, ...);
  78.     
  79.     // messaging separated into two distinct operations...
  80.     (*objc_msgLookup(obj, sel))(obj, sel, arg1);
  81.  
  82. It might be nice to override objc_msgSend on a per class basis as well, depending on the application.
  83.  
  84. snaroff.
  85.  
  86. Date: Mon, 21 Sep 92 20:05:34 -0400
  87. From: rms@gnu.ai.mit.edu (Richard Stallman)
  88. To: Steve_Naroff@next.com
  89. Cc: gsk@marble.com, gnu-objc@prep.ai.mit.edu
  90. Subject: extending message lookup vs. open coding
  91.  
  92. In our runtime, dispatch and lookup are separate.  That was easy to
  93. implement in C.
  94.  
  95. My plan is to replace objc_msgSend with a couple of array and
  96. structure refs.  So far I don't see that replacing objc_msgSend
  97. gives a real increase in power.
  98.  
  99. From: Steve_Naroff@NeXT.COM (Steve Naroff)
  100. Date: Tue, 22 Sep 92 11:25:18 -0700
  101. To: rms@gnu.ai.mit.edu (Richard Stallman)
  102. Subject: Re: extending message lookup vs. open coding
  103. Cc: gsk@marble.com, athan@object.com
  104.  
  105.  
  106. > In our runtime, dispatch and lookup are separate.  That was easy to
  107. implement in C.
  108.  
  109. This sounds good, I should probably learn more about the FSF runtime. Have you considered message forwarding (i.e. delegation)? This (unfortunately) cannot be done entirely in C. I hope the FSF runtime plans to implement forwarding, it is a powerful feature can enable some interesting things (like Distributed Objects).
  110.  
  111. > My plan is to replace objc_msgSend with a couple of array and
  112. structure refs. So far I don't see that replacing objc_msgSend gives a real increase in power.
  113.  
  114. I'm not saying this is a panacea. In my mind, the increase in power comes down to enabling a new class of debug/analysis tools that can be written/used during development. The other is obviously changing the semantics of messaging, which is less important to me personally.
  115.  
  116. Ultimately, the decision should be made by weighing the performance win by open coding the dispatcher vs. the flexibility of being able to replace the dispatcher.
  117.  
  118. snaroff.
  119.  
  120. Date: Tue, 22 Sep 92 15:08:28 -0400
  121. From: rms@gnu.ai.mit.edu (Richard Stallman)
  122. To: Steve_Naroff@NeXT.COM
  123. Cc: gsk@marble.com, athan@object.com
  124. Subject: extending message lookup vs. open coding
  125.  
  126.     I hope the FSF runtime  
  127.     plans to implement forwarding, it is a powerful feature can enable  
  128.     some interesting things (like Distributed Objects).
  129.  
  130. Sorry, this is beyond what I have time to think about.
  131.  
  132.     I'm not saying this is a panacea. In my mind, the increase in power  
  133.     comes down to enabling a new class of debug/analysis tools that can  
  134.     be written/used during development.
  135.  
  136. I think this can be done anyway, by modifying the dispatch tables.
  137.  
  138. Date: Tue, 22 Sep 92 06:40:52 -0700
  139. From: Dennis Glatting <seattle-ni-srvr!dglattin@trirex.COM>
  140. To: uunet!next.com!Steve_Naroff@uunet.UU.NET (Steve Naroff)
  141. Subject: extending message lookup vs. open coding
  142. Cc: Geoffrey S. Knauth <gsk@marble.com>, gnu-objc@prep.ai.mit.edu
  143.  
  144. > Allowing applications to conveniently override
  145. > objc_msgSend() is a very useful feature that
  146. > underscores the benefits of having a central
  147. > dispatcher. I don't like the idea of "open coding" calls
  148. > to objc_msgSend(), it is not clear that this will result
  149. > in any noticeable performance improvement (it could
  150. > result in significant code expansion and hurt
  151. > performance, who knows).
  152.  
  153. The GNU version of objc_msgSend returns a method address.  Therefore, the compiler produces code in this fashion:  stack the *method's* variables;  stack objc_msgSend's variables;  call objc_msgSend, clean up the stack tail;  then call the method.  One problem however -- although the problem may be philosophical -- is that this impacts defer-pop optimization.
  154.  
  155. The GNU objc_msgSend works by array look up.  The advantage to this is speed (the array operations in the run time are nicely optimized by the compiler too).  Therefore, what we are swapping (code bulk) by open coding the method dispatch is additional stacking, a subroutine call, and some stack clean up verses an array look up.
  156.  
  157. I suspect that, if we open code method dispatch, we'll approach the theoretical minimum dispatch overhead of 2 (currently it is 3).
  158.  
  159.     [paragraph deleted ]
  160.  
  161. > If the goal is making the message dispatcher easier to
  162. > customize, writing the assembler magic to reuse the
  163. > stack frame (and not disrupt any registers used by the
  164. > compiler) might be intimidating to many application
  165. > writers. Separating "lookup" from "dispatch" removes
  166. > the assembler hacking (however makes the post op
  167. > functionality hard).
  168.  
  169. The design of the run time is purposely portable (aka: no assembly language).  There are several problems with assembly language:  code maintenance and dealing with various processor architectures -- not all processors are stack based.
  170.  
  171. -dpg
  172.  
  173. Date: Wed, 23 Sep 92 23:16:16 -0700
  174. From: Dennis Glatting <seattle-ni-srvr!dglattin@uunet.UU.NET>
  175. To: Geoffrey S. Knauth <uunet!marble.com!gsk@uunet.UU.NET>
  176. Subject: Re: extending message lookup vs. open coding
  177.  
  178. > I found myself wondering what would happen if NeXT
  179. > messaging had 3:1 overhead, and your system had only 2:1.
  180. > It seems there's room for everyone to improve.
  181. >
  182.  
  183. Steve may be right but open coding should be evaluated.
  184.  
  185. -dpg
  186.  
  187. Date: Sun, 21 Feb 1993 21:34:10 +0100
  188. From: Kresten Krab Thorup <krab@iesd.auc.dk>
  189. Reply-To: gnu-objc@gnu.ai.mit.edu
  190. To: gnu-objc@gnu.ai.mit.edu
  191. Subject: How to do lookup (or: Is the simple array approach acceptable?)
  192. Cc: krab@iesd.auc.dk, rms@gnu.ai.mit.edu
  193.  
  194. The currently distributed runtime uses a very simple array mechanism
  195. for method lookup.  Each class has an array which is as large as the
  196. maximum number of selectors in any class.  The lookup is then simply
  197. to get the element at the index corresponding to the selector number.
  198.  
  199. As noted by several people, this is approach is probably not good
  200. enough if there is a lot of selectors in the system, since it will
  201. take a lot of space.  Further more, if dynamic loading is implemented
  202. some time in the future, we will have to realloc all these dispatch
  203. arrays when we load in new code to make room for the new selectors. 
  204.  
  205. An alternative to the simple minded full size array would be the
  206. ollowing data structure.  I don't know if it already has a name ssince
  207. i invented it myself.  I will call it a `doubly indexed array'.  Using
  208. this structure we can probably save a lot of space, and time in the
  209. initialization phase.  It will however cost a bit more to do the
  210. lookup.  Is this what is known in gcc as an `obarray'?  Maybe, we'll
  211. see... 
  212.  
  213. The basic idea is that an DIarray is keeping track of small intervals
  214. (kept in buckets) of the full range of the array.  If a such bucket is
  215. empty, it will point to a special bucket, the `empty_bucket'.  Hence,
  216. an array with no elements in it will occupy only one bucket no matter
  217. how big it is.
  218.  
  219. Besides, it is relatively cheap to realloc a such array, since we'll
  220. only have to realloc the first indirect table.  If the bucketsize is
  221. kept relatively small, most buckets will be empty if used as dispatch
  222. table in objc, since any class will only implement a subset of the
  223. known selectors.
  224.  
  225. Since it is convenient, I will describe the DIarray data structure as
  226. an objective C class.  The real implementation should of cause be done
  227. in C.  The code is _not_ optimized, only coded for clarity!
  228.  
  229.   @interface DIarray 
  230.    {
  231.      short max_elements;       /* elements in array */
  232.      short bucket_size;        /* size of one bucket */
  233.      id* empty_bucket;     /* the empty bucket */
  234.      id** bucket_table;    /* array holding pointers to buckets */
  235.    }
  236.  
  237.    +newWith: (short)size bSize: (short)bucket_size missingElem: missing_elem;
  238.  
  239.    -atIndex: (short)index;
  240.    -atIndex: (short)index put: anObject;
  241.  
  242.   @end
  243.  
  244. The implementation follows right after:
  245.  
  246.   @implementation DIarray
  247.  
  248.   - atIndex: (short)index
  249.   {
  250.      return (bucket_table[index/bucket_size])[index%bucket_size];
  251.   }
  252.  
  253.   /* If bucket_size is power of two, the divide and modulus operations
  254.   /* in atIndex: can be coded as bit-shift and bit-and.  This */
  255.   /* would be a significant speedup on most architectures.  */
  256.  
  257.   +newWith: (short)size bSize: (short)bucket_size missingElem: missing_element;
  258.     {
  259.       short num_buckets = (size%bucket_size)+1;
  260.       short counter;
  261.       assert <<bucket_size is power of two>>;
  262.  
  263.       self = [super alloc];  // Allocate me
  264.  
  265.       // Initialize elements
  266.       max_elements = size;
  267.       bucket_size  = bucket_size;
  268.       bucket_table = (id**)malloc(num_buckets*sizeof(id));
  269.       empty_bucket = (id*)malloc(bucket_size*sizeof(id));
  270.  
  271.       // fill empty bucket with missing_element 
  272.       for(counter=0; counter<bucket_size; counter++)
  273.         empty_bucket[counter] = missing_element;
  274.  
  275.       // Let all bucket entries point to empty_bucket 
  276.       for(counter=0; counter < num_buckets; counter++)
  277.         bucket_table[counter] = empty_bucket;
  278.  
  279.       return self;
  280.     }
  281.       
  282.     /* the store operation is a bit more complicated, since it has to
  283.     /* check if the bucket being put into is the `empty_bucket' */
  284.     /* and in that case allocate a new bucket for that slot */
  285.  
  286.     - atIndex: (short)index put: anObject
  287.     {
  288.       id** bucket = &(bucket_table[index/bucket_size]);
  289.       if(*bucket==empty_bucket) {
  290.         *bucket=(id*)malloc(bucket_size*sizeof(id));
  291.     bcopy(empty_bucket, *bucket, bucket_size*sizeof(id));
  292.       }
  293.       (*bucket)[index%bucket_size] = anObject;
  294.     }
  295.  
  296.   @end -- DIarray
  297.  
  298.  
  299. As noted, the modulus and divide operations needed to calculate the
  300. position in the array can be done as bit operations if the bucket size
  301. is a power of two.  Further more, the allocation of buckets could be
  302. done from a pool of buckets to improve performance at that point.  The
  303. code for resizing the array is not included above, but if the
  304. structure is clear to the reader that code is obvious.
  305.  
  306. I am sure, that using a structure like this for the dispatch table
  307. will use much less space, since many entries which point to
  308. `__objc_missingMethod' would possibly be in the `empty_bucket'.
  309. Further more it is very cheap to resize the structure, as we will only
  310. have to realloc the bucket_table.
  311.  
  312. Comments would be appreciated.
  313.  
  314. /Kresten
  315.  
  316. Kresten Krab Thorup               |       /   | E-mail : krab@iesd.auc.dk
  317. Institute of Electronic Systems   |   ,-'/(   | S-mail : Sigrid Undsetsvej 226A
  318. Aalborg University                |  /  |  \  |          9220 Aalborg \O
  319. Fr. Bajers vej 7, DK-9220 Aalb    |  A  U  C  |          Denmark
  320. -------------------------------------------------------------------------------
  321.                Member of The League for Programming Freedom
  322.  
  323. Return-Path: <burchard@localhost.gw.umn.edu>
  324. Date: Sun, 21 Feb 93 16:43:20 -0600
  325. From: Paul Burchard <burchard@localhost.gw.umn.edu>
  326. To: krab@iesd.auc.dk
  327. Subject: Re: How to do lookup (or: Is the simple array approach acceptable?)
  328. Cc: gnu-objc@gnu.ai.mit.edu
  329. Reply-To: burchard@geom.umn.edu
  330.  
  331. The "sparse array" you desire is, as you suspect, not new---it's  
  332. essentially what is called a "hash table".
  333.  
  334. For an overview of design issues, see Seltzer and Yigit, USENIX  
  335. Winter 1991 (this paper can be obtained from the BSD sources along  
  336. with the hashing package described there).  Implementing a hash table  
  337. as a huge array is one end of the spectrum of hash table design,  
  338. which attempts to trade space for time.  Your DIArray is another,  
  339. more intermediate, point on the spectrum.
  340.  
  341. The "correct" hash table design depends on the actual lookup patterns  
  342. that the sparse array experiences during use.  If you have a special  
  343. hash table design that is more efficient for sel->imp lookup than the  
  344. generic one currently used, that's great!
  345.  
  346. --------------------------------------------------------------------
  347. Paul Burchard    <burchard@geom.umn.edu>
  348. ``I'm still learning how to count backwards from infinity...''
  349. --------------------------------------------------------------------
  350.  
  351. Return-Path: <krab@iesd.auc.dk>
  352. Date: Tue, 23 Feb 1993 05:47:52 +0100
  353. From: Kresten Krab Thorup <krab@iesd.auc.dk>
  354. To: burchard@geom.umn.edu, rms@gnu.ai.mit.edu, gnu-objc@gnu.ai.mit.edu
  355. Subject: The sparse arrays is it!
  356. Cc: krab@iesd.auc.dk
  357.  
  358. I just implemented a quite sophisticated sparse array (somewhat like I
  359. explained in my last article), and it surely looks like this is going
  360. to be a hit!  I have been experimenting with different ideas, and has
  361. come up with a version, which uses reference counting and lazy copying
  362. (copy-on-write).  `My' copy-on-write will actually only copy the
  363. bucket you are writing to, if you write something different than what
  364. is already there.  At the same time, all the empty buckets will point
  365. to the same empty bucket.
  366.  
  367. If you think of all the dispatch tables in a running system, and let
  368. the one in `Object' be the very `super' dispatch table.  Now, if all
  369. other tables are `lazy' copies of this one (following the inheritance
  370. hierachy), we can save even more space and time, since buckets
  371. (belonging to a super table) which are not changed in the subclasses
  372. (methods not overwritten, only inherited) can be shared between the
  373. tables, and hence only allocated once!  This is really great--there
  374. should be a good chance that buckets can simply be reused in
  375. subclasses.
  376.  
  377. The lookup (dispatch) is going to be fast. Trying it out on a Sun
  378. (sparc), it comes out as only 5 assembler instructions, which is for
  379. sure better than any dynamic language has seen before (perhaps Self
  380. can do better, but they do nasty tricks).  The 5 instructions is based
  381. on that the parameters to the lookup function are already in
  382. registers, since I expanded the function inline.
  383.  
  384. Isn't this just great?
  385.  
  386. /Kresten
  387.  
  388. Return-Path: <krab@iesd.auc.dk>
  389. Date: Tue, 23 Feb 1993 07:28:34 +0100
  390. From: Kresten Krab Thorup <krab@iesd.auc.dk>
  391. To: rms@gnu.ai.mit.edu (Richard Stallman)
  392. In-Reply-To: <9302230537.AA09054@geech.gnu.ai.mit.edu>
  393. Subject: The sparse arrays is it!
  394. Cc: krab@iesd.auc.dk, gnu-objc@gnu.ai.mit.edu
  395.  
  396. Richard Stallman writes:
  397. >It sounds good.  But please think of how this fits in with
  398. >the multi-thread locking scheme that I designed.
  399.  
  400. As far as I can see, there are no serious problems concerning my new
  401. tables in this direction.
  402.  
  403. However, I do se some problems in _general_ in your design, which have
  404. to be thought about.  What happens if it is not only a new class, but
  405. a category which is loaded dynamically?  (That's a class add-on) If
  406. the class which gets added something has subclasses, we will have to
  407. figure out in which order the class and the subclasses should have
  408. their new dispatch tables enabled--since we don't have locking on
  409. lookup, we'll have to figure out the ordering.  I don't have an answer
  410. ready at hand, perhaps someone on the list?
  411.  
  412. /Kresten
  413.  
  414. From: Steve_Naroff@NeXT.COM (Steve Naroff)
  415. Date: Thu, 25 Feb 93 12:53:58 -0800
  416. To: Kresten Krab Thorup <krab@iesd.auc.dk>
  417. Subject: Re: Optimizing the GNU objc runtime [long]
  418. Cc: gnu-objc@gnu.ai.mit.edu
  419.  
  420.  
  421. Here is some data for a "typical" application on a NeXT (linked with the standard shared library of objects that we supply):
  422.  
  423. 200-300 classes
  424. 6000-7000 method implementations
  425. 2000-3000 selectors (unique method names)
  426.  
  427. Many of these are classes are "private"...nevertheless, it is important that the runtime scale to support a system of this magnitude. It seems like you have already abandoned the idea of a per-class vector that has room for all possible selectors. This is good, since my "back of the envelope" calculation comes out to 3.2 megabytes to store all the caches (using 200 classes, 2000 selectors in the computation)! Let me know if I misinterpreted your proposal.
  428.  
  429. It sounds like the sparse array idea might reclaim much of this overhead. I would be interested in how much space that works out to be. The NeXT runtime typically consumes between 20-40 kilobytes for the per-class caches. The space occupied is proportional to the classes/methods used by the application. The simple rule is "pay if you play". In fact, even though there are 200-300 classes linked into an application, only 30-50% are typically used. I think any scalable system must incur mininal overhead (on a per-class basis) until the class is actually used.
  430.  
  431. Another method for doing dynamic dispatch (that is similar in spirit to your scheme) is to have a vector that is maintained on a per-app basis (not per-class) that has an entry for each selector. Each entry would contain a chain of classes that implement the selector...in practice, this chain is very short. A simple way to view this is go from the selector->class, rather than class->selector. I'm not convinced it statisfies your performance goals, but it is another way to look at the problem.
  432.  
  433. I think it is worthwhile goal to improve the performance of dispatching messages in Objective-C. Just be aware that it is a classic "time vs. space" problem...and saving time without carefully considering space doesn't work. Most computers are getting a lot faster without adding significantly more memory...this should influence the tradeoffs we make when designing the new runtime (my perspective, of course).
  434.  
  435. In the past, we have spent time optimizing memory utilization and improving locality of reference. This includes the data generated by the compiler as well as caches that are dynamically allocated (which are often "hot" for commonly used classes). We also developed a program called "objcopt" that improves launch time by building the selector hash table and "freeze drying" it in the executable for each shared library that we ship.
  436.  
  437. Given the portability goals of the project, I can't say that many of these apply to the GNU runtime...I just wanted to let you know what problems we work on wrt optimizing the NeXT Objective-C runtime.
  438.  
  439. btw...the NeXT runtime sends the "+initialize" lazily, was the change to execute before main() made intentionally?
  440.  
  441. regards,
  442.  
  443. snaroff.
  444.  
  445. Date: Thu, 25 Feb 93 18:04:54 -0500
  446. From: rms@gnu.ai.mit.edu (Richard Stallman)
  447. To: Steve_Naroff@NeXT.COM
  448. Cc: krab@iesd.auc.dk, gnu-objc@gnu.ai.mit.edu
  449. In-Reply-To: <9302252054.AA20409@oz.NeXT.COM> (Steve_Naroff@NeXT.COM)
  450. Subject: Optimizing the GNU objc runtime [long]
  451.  
  452.     In fact, even though there are 200-300 classes linked into  
  453.     an application, only 30-50% are typically used. I think any scalable  
  454.     system must incur mininal overhead (on a per-class basis) until the  
  455.     class is actually used. 
  456.  
  457. It ought to be possible to delay setting up a class's method dispatch
  458. table until the first time it is instantiated.  Until then you need
  459. only the dispatch mechanism for the methods that act on the class
  460. rather than on an instance.  I expect there will be much fewer
  461. operations of that sort.
  462.  
  463.  
  464. Date: Fri, 26 Feb 1993 00:53:14 +0100
  465. From: Kresten Krab Thorup <krab@iesd.auc.dk>
  466. To: Steve_Naroff@NeXT.COM (Steve Naroff)
  467. Cc: Kresten Krab Thorup <krab@iesd.auc.dk>, gnu-objc@gnu.ai.mit.edu
  468. In-Reply-To: <9302252054.AA20409@oz.NeXT.COM>
  469. Subject: Re: Optimizing the GNU objc runtime [long]
  470.  
  471. Steve Naroff writes:
  472. > It seems like you have already abandoned the idea of a  
  473. >per-class vector that has room for all possible selectors. This is  
  474. >good, since my "back of the envelope" calculation comes out to 3.2  
  475. >megabytes to store all the caches (using 200 classes, 2000 selectors  
  476. >in the computation)! Let me know if I misinterpreted your proposal.
  477.  
  478. You're right.  Obviously we cannot have an array with domaine of all
  479. selectors for each class.   I have been in this belief for some time
  480. now.  The comments in the `Optimizing ...' article were to make it
  481. clear that this was not the subject of the paper.
  482.  
  483. >It sounds like the sparse array idea might reclaim much of this  
  484. >overhead. I would be interested in how much space that works out to  
  485. >be.
  486.  
  487. I have a prototype of the runtime up running using sparse arrays for
  488. dispatch.  Ok, I compiled the first program successfull 10 minutes
  489. ago, so I'll have to test it a bit more before it's worth distributing!
  490.  
  491. >The NeXT runtime typically consumes between 20-40 kilobytes for  
  492. >the per-class caches. The space occupied is proportional to the  
  493. >classes/methods used by the application. The simple rule is "pay if  
  494. >you play". In fact, even though there are 200-300 classes linked into  
  495. >an application, only 30-50% are typically used. I think any scalable  
  496. >system must incur mininal overhead (on a per-class basis) until the  
  497. >class is actually used. 
  498.  
  499. This mechanism could be adopted to install the tables incrementally,
  500. if only we had objc_msgSendv!!!  Please, if someone is into assember
  501. and gcc internals do it!  I dont know far enough about assember to do
  502. it, who could we possibly ask?
  503.  
  504. I am very excited to see some performace result from my new runtime.
  505. Sadly enough I will be gone for the weekend :-)  
  506.  
  507. I myself is actually very fond of using caches and ordinary dynamic
  508. lookup.  This has one big advantage which my scheme will have to pay
  509. for: It is simple to add methods to excisting classes at runtime.  On
  510. the other hand, by installing magic handlers in the dispatch table, my
  511. scheme allows the programmer to install his own messenger on class
  512. basis. I guess to think of a nice API for this ..
  513.  
  514. >Given the portability goals of the project, I can't say that many of  
  515. >these apply to the GNU runtime...I just wanted to let you know what  
  516. >problems we work on wrt optimizing the NeXT Objective-C runtime.
  517.  
  518. I dont know for sure.  I guess most things written in GNU objc will
  519. probably NOT be large applications with zillions of classes and using
  520. dynamic loading.  At least it will take some time before this will be
  521. the case, since class libraries and loading stuff is not easily
  522. accessible... Several operating systems provide facilities -- I know,
  523. but far from all.
  524.  
  525. >btw...the NeXT runtime sends the "+initialize" lazily, was the change  
  526. >to execute before main() made intentionally?
  527.  
  528. Initially, yes.  The problem is that if you access variables
  529. initialized in "+initialize" directly (without a message call) you are
  530. not sure about the state of these.  After having talked to some fellow
  531. students, I may have changed my mind, but one must enforce the
  532. constraint that such variables may only be accessed through method
  533. invocations.  It may be a problem, if class variables are introduced,
  534. that you can say MyClass->aVariable, but aVariable is assigned its
  535. default value in "+initialize" !
  536.  
  537. I hope to be able to release a beta version of my new runtime within a
  538. week or so.  It may be that someone are interested in studying it? :-)
  539.  
  540. /Kresten
  541.  
  542. Date: Fri, 26 Feb 1993 01:21:01 +0100
  543. From: Kresten Krab Thorup <krab@iesd.auc.dk>
  544. To: rms@gnu.ai.mit.edu (Richard Stallman)
  545. Cc: Steve_Naroff@NeXT.COM, krab@iesd.auc.dk, gnu-objc@gnu.ai.mit.edu
  546. In-Reply-To: <9302252304.AA06929@mole.gnu.ai.mit.edu>
  547. Subject: Optimizing the GNU objc runtime [long]
  548.  
  549. Richard Stallman writes:
  550. >It ought to be possible to delay setting up a class's method dispatch
  551. >table until the first time it is instantiated.  Until then you need
  552. >only the dispatch mechanism for the methods that act on the class
  553. >rather than on an instance.  I expect there will be much fewer
  554. >operations of that sort.
  555.  
  556. This is easily done using the sparse array scheme.  To do it, we would
  557. initially install a dispatch table where the empty bucket is filled
  558. with some function which installs the right dispatch table and forwards
  559. the message.  We will however need the ability to do forwarding.  
  560.  
  561. Do you think we can find someone who'd like to implement a forwarding
  562. primitive?  At least the primitive one (__builtin_forward) we've been
  563. talking about which does not allow one to do anything after the
  564. forward.  Using this we could implement the lazy dispatch table setup,
  565. as well as lazy initialization.
  566.  
  567. /Kresten
  568.  
  569.  
  570. Date: Mon, 1 Mar 1993 18:01:49 +0100
  571. From: Kresten Krab Thorup <krab@iesd.auc.dk>
  572. To: gnu-objc@gnu.ai.mit.edu
  573. Subject: Sparse array stats
  574. Cc: krab@iesd.auc.dk
  575.  
  576. Hi again
  577.  
  578. I have been doing some statistics on my sparse array dispatch
  579. mechanism.  For the test I used the public methods and classes of the
  580. NeXT appkit.  This makes 76 classes and 1661 distinct selectors.  This
  581. includes 2371 distinct instance methods and 258 distinct factory
  582. methods.
  583.  
  584. The best test results for fixed size dispatch tables look like this:
  585.  
  586.    Bucket size: 32 entries
  587.  
  588.    Number of classes: 76
  589.    Number of distinct selectors: 1661
  590.    Number of distinct instance impls: 2371
  591.    Number of distinct factory impls: 258
  592.  
  593.    Number of buckets for itables: 417
  594.    Number of buckets for ftables: 124
  595.  
  596.    Memory used for ibuckets: 53k
  597.    Memory used for fbuckets: 15k
  598.    Memory used for sparse indices: 32k
  599.  
  600.    ======================================
  601.    Total memory used for dispatch: 100k
  602.  
  603. I have tried sorting the selectors by number of redefinitions, so that
  604. methods often redefined are grouped together.  Surprisingly enough
  605. this does not make the result better!  
  606.  
  607. There are several ways to reduce the amount of memory used.  As
  608. proposed by Steve Naroff, we can delay the initialization of dispatch
  609. tables for instance methods (which takes the most memory) until an
  610. instance of the specific class is actually used.  The simple way is to
  611. check that is installed in `class_createInstance'.  We could make it
  612. even more sophisticated, by actually installing the methods one by one
  613. as they are called, but this requires additional code in the
  614. messenger.
  615.  
  616. Another approach is to sort the selectors by number of definitions,
  617. and then have dispatch tables of different sizes, so that subclasses
  618. expand the dispatch table by the number of new methods.  The next test
  619. does this, and also used sorting of the selectors by number of
  620. redefinitions.  This approach reduces the memory usage for the sparce
  621. indices noticably, and combined with a smaller bucket size (as small
  622. as 8, actually) it gives something as good as this:
  623.  
  624.    Bucket size: 8 entries
  625.  
  626.    Number of classes: 76
  627.    Number of distinct selectors: 1661
  628.    Number of distinct instance impls: 2371
  629.    Number of distinct factory impls: 258
  630.  
  631.    Number of buckets for itables: 928
  632.    Number of buckets for ftables: 188
  633.  
  634.    Memory used for ibuckets: 32k
  635.    Memory used for fbuckets: 6k
  636.  
  637.    Memory used for sparse i-indices: 12k
  638.    Memory used for sparse f-indices: 7k
  639.  
  640.    ========================================
  641.    Total memory usage for dispatch:  57k
  642.    
  643. This is really great!  Half the size of the static approach, and
  644. somewhat closer to the typical NeXT usage of 30-40k in total [snaroff].
  645. This does however require an additional range test in the lookup
  646. function, so that selectors above the table size are taken from the
  647. `empty bucket'. 
  648.  
  649. In my oppinion we should use the latter, and live with the extra check
  650. in the messenger.  This is still a whole lot faster than a caching
  651. approach.  Perhaps it should even be combined with the late
  652. initialization mechanism.
  653.  
  654. Comments appreciated.
  655.  
  656. /Kresten
  657.  
  658.